package org.copper.objectpool
{
	import org.copper.utils.ConditionUtil;
	import org.copper.utils.StringUtils;

	public class ObjectPool
	{	
		private var m_stcak:Vector.<Object> = new Vector.<Object>();
		private var m_onRequire:String;
		private var m_onSubmit:String;
		private var m_onDispose:String;
		private var m_factory:Function;
		private var m_maxCapacity:uint;
		
		/**
		 * Utility class for object pooling.
		 * */
		public function ObjectPool(factoryMethod:Function = null, maxCapacity:uint = 0, onRequire:String = "onRequire", onSubmit:String = "onSubmit", onDispose:String = "onDispose")
		{
			super();
			ConditionUtil.assert(StringUtils.isNullOrEmpty(onRequire) == false, "'onRequire' handler name can not be null or empty.");
			ConditionUtil.assert(StringUtils.isNullOrEmpty(onSubmit) == false, "'onSubmit' handler name can not be null or empty.");
			ConditionUtil.assert(StringUtils.isNullOrEmpty(onDispose) == false, "'onDispose' handler name can not be null or empty.");
			
			m_factory = factoryMethod;
			m_maxCapacity = maxCapacity;
			m_onRequire = onRequire;
			m_onSubmit = onSubmit;
			m_onDispose = onDispose;
		}
		
		public function disposeAll():void
		{
			const len:uint = m_stcak.length;
			for(var i:uint = 0; i < len; i++)
			{
				const obj:Object = m_stcak.pop();
				const onDispose:Function = obj[m_onDispose];
				ConditionUtil.assert(onDispose != null, "'onDispose' handler not found.");
				onDispose();
			}
		}
		
		public function submit(object:Object):void
		{
			ConditionUtil.assert(object != null, "'null' can not be submitted.");
			const onSubmit:Function = object[m_onSubmit];
			ConditionUtil.assert(onSubmit != null, "'onSubmit' handler not found.");
			onSubmit();
			if (m_maxCapacity > 0)
			{
				if (m_stcak.length == m_maxCapacity)
				{
					const onDispose:Function = object[m_onDispose];
					ConditionUtil.assert(onDispose != null, "'onDispose' handler not found.");
					onDispose();
				}
				else
				{
					m_stcak.push(object);
				}
			}
			else
			{
				m_stcak.push(object);
			}
		}
		
		public function require(...args):Object
		{
			var result:Object;
			if (m_stcak.length < 1)
			{
				ConditionUtil.assert(m_factory != null, "Object pool is empty and factory method is undefined.");
				result = args.length > 0?m_factory(args):m_factory();
			}
			else
			{
				result = m_stcak.pop();
			}
			
			const onRequire:Function = result[m_onRequire];
			ConditionUtil.assert(onRequire != null, "'onRequire' handler not found.");
			onRequire();
			return result;
		}
		
		public function get capacity():uint
		{
			return m_stcak.length;
		}
	}
}